import type Mapless from '..';
import IdHub, { type IdHubType } from '../core/IdHub';
import { Manager } from '../core/manager';
import type { BoundaryOptions, ManagerOptions } from '../index.d';

type BoundaryIdHubType = IdHubType & {
  layerId: string;
  maskLayerId: string;
  sourceId: string;
  maskSourceId: string;
};

class BoundaryManager extends Manager<BoundaryIdHubType> {
  private boundary: GeoJSON.GeoJSON | undefined;

  constructor(mapless: Mapless, options?: ManagerOptions) {
    super(mapless, options, 'boundary');
    this.onInitialized();
  }

  override createIdHub(_idHub?: IdHubType) {
    const newHub = new IdHub({
      layers: {
        layerId: 'dzmap-boundary-layer',
        maskLayerId: 'dzmap-boundary-mask-layer',
      },
      sources: {
        sourceId: 'dzmap-boundary-source',
        maskSourceId: 'dzmap-boundary-mask-source',
      },
    }) as BoundaryIdHubType;
    super.createIdHub(newHub);
  }

  override initLayers() {
    if (!this.mapless) return;
    // 添加数据源
    const sid = this.idHub?.sourceId;
    this.mapless.addEmptySource(sid);
    this.mapless.addLayer({
      id: this.idHub?.layerId,
      type: 'line',
      source: sid,
      paint: {
        'line-color': ['get', 'line-color'],
        'line-width': ['get', 'line-width'],
        'line-opacity': 1,
      },
    });
    // 添加遮罩
    const msid = this.idHub?.maskSourceId;
    this.mapless.addEmptySource(msid);
    this.mapless.addLayer({
      id: this.idHub?.maskLayerId,
      type: 'fill',
      source: msid,
      paint: {
        'fill-color': ['get', 'fill-color'],
        'fill-opacity': ['get', 'fill-opacity'],
      },
    });
  }

  drawBoundary(options: BoundaryOptions) {
    const {
      data,
      strokeColor = '#3498db',
      strokeWidth = 3,
      fillColor = '#2488cb',
      fillOpacity = 0.45,
    } = options;

    const geoJson = data as GeoJSON.FeatureCollection;

    const features = geoJson.features;
    features.forEach((f) => {
      const props = f.properties || {};
      if (!props['line-color']) {
        props['line-color'] = strokeColor;
      }
      if (!props['line-width']) {
        props['line-width'] = strokeWidth;
      }
      f.properties = props;
    });
    this.boundary = geoJson;
    this.mapless?.setData(this.idHub?.sourceId, geoJson);
    // TODO 小心多个分离轮廓的问题
    const maskJson = this.genMaskData(geoJson, fillColor, fillOpacity);
    this.mapless?.setData(this.idHub?.maskSourceId, maskJson);
  }

  // TODO  addBoundary updateBoundary removeBoundary

  private genMaskData(
    geoJson: GeoJSON.GeoJSON,
    fillColor = "'#2488cb'",
    fillOpacity = 0.45,
  ): GeoJSON.GeoJSON {
    // 提取features数组，支持FeatureCollection和单个Feature两种格式
    const features =
      geoJson.type === 'FeatureCollection'
        ? geoJson.features
        : [geoJson as GeoJSON.Feature];
    const maskSourceData = {
      type: 'Feature',
      geometry: {
        type: 'Polygon',
        coordinates: [
          // 外围世界范围（矩形）
          [
            [-180, -90],
            [180, -90],
            [180, 90],
            [-180, 90],
            [-180, -90],
          ],
          // 边界（挖洞）
          ...features.flatMap((feature) => {
            if (feature.geometry.type === 'MultiPolygon') {
              return feature.geometry.coordinates.map((polygon) => polygon[0]);
            }
            return [];
          }),
        ],
      },
      properties: {
        'fill-color': fillColor,
        'fill-opacity': fillOpacity,
      },
    };
    return maskSourceData as GeoJSON.GeoJSON;
  }

  toggleMask(visible = true) {
    const mlid = this.idHub?.maskLayerId;
    if (this.mapless?.getLayer(mlid)) {
      this.mapless.setLayoutProperty(
        mlid,
        'visibility',
        visible ? 'visible' : 'none',
      );
    }
  }

  override fitBounds = () => {
    const features =
      this.boundary?.type === 'FeatureCollection'
        ? this.boundary.features
        : [this.boundary as GeoJSON.Feature];

    const coordinates = features.flatMap((feature) => {
      if (feature.geometry.type === 'MultiPolygon') {
        return feature.geometry.coordinates.flatMap((polygon) =>
          polygon[0].map((pos) => [pos[0], pos[1]] as [number, number]),
        );
      }
      return [];
    });

    if (coordinates.length > 0) {
      this.mapless?.fitBounds(coordinates);
    }
  };

  override destroy(): void {
    super.destroy();
    this.boundary = undefined;
  }
}

export default BoundaryManager;

export const attachBoundary = (mapless: Mapless | undefined, name?: string) =>
  mapless?.attach(BoundaryManager, name);
export const getBoundary = (mapless: Mapless | undefined, name?: string) =>
  mapless?.get(BoundaryManager, name);
export const detachBoundary = (mapless: Mapless | undefined, name?: string) =>
  mapless?.detach(BoundaryManager, name);
